<?php

namespace Simanx\Spes\Dto;

use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use Simanx\Spes\Dto\Exceptions\DtoResolveException;

class DtoResolver
{
    /**
     * @throws DtoResolveException
     */
    public function resolve($class, array|Request $data)
    {
        try {
            $reflectionClass = new \ReflectionClass($class);
            if ($data instanceof Request) {
                $request = $data;
                /** @var Route $route */
                $route = $request->getRouteResolver()();
                $data = $route->parameters + ($request->isMethod('GET') ?
                        $request->query() : ($request->isJson() ? $request->json()->all() : $request->post()));
            }

            /** @var Dto $obj */
            $obj = new $class();
            foreach ($data as $key => $value) {
                if (!$reflectionClass->hasProperty($key)) {
                    continue;
                }

                $property = $reflectionClass->getProperty($key);
                if (!$property->hasType()) {
                    $obj->setAttribute($key, $value);
                    continue;
                }

                if ($value === '') {
                    continue;
                }

                switch ($propertyType = $property->getType()->getName()) {
                    case 'string':
                    case 'int':
                    case 'float':
                        $obj->setAttribute($key, $value);
                        break;
                    case 'bool':
                        $obj->setAttribute($key, !in_array($value, ['0', 'false', 'off', 'no']));
                        break;
                    case 'array':
                        $obj->setAttribute($key, json_decode($value, true));
                        break;
                    case 'object':
                        $obj->setAttribute($key, json_decode($value));
                        break;
                    default:
                        // 特定class对象
                        if (class_exists($propertyType) && is_subclass_of($propertyType, Dto::class)) {
                            $obj->setAttribute($key, $this->resolve($propertyType, is_array($value) ? $value : json_decode($value, true)));
                        } else {
                            $obj->setAttribute($key, new $class(json_decode($value, true)));
                        }
                        break;
                }
            }
        } catch (\Exception $_) {
            throw new DtoResolveException("dto resolve error, class: $class");
        }

        return $obj;
    }
}
